본문으로 건너뛰기

오늘 한 일

  • Android 앱개발
  • 비동기처리 공부

Coroutine 사용

코루틴이란

: 비동기처리를 위해 사용되는 개념

협력형 멀티태스킹

Co = 협력 routine = 태스크 ⇒ 협력해서 "태스크"를 처리하는 것


Routine이란?

  • Main Routine
  • Sub Routine

*일반적 매커니즘 with Routines
메인루틴 →[ 서브루틴 호출 → 서브루틴 실행 → 서브루틴 탈출 ]→ 메인루틴

스크린샷 2023-08-18 오전 12 06 55

⇒ System Stack에 서브루틴이 push 되어 스레드가 block되어 처리된다
⇒ 서브루틴 호출, 서브루틴 탈출은 오직 한 번 발생한다


*with CoRoutines

메인루틴 → 서브루틴 호출 → 서브루틴 실행 → 서브루틴 탈출 → 메인루틴 실행 → … → 서브루틴 호출 → 서브루틴 탈출…

스크린샷 2023-08-17 오전 11 26 04

⇒ 서브루틴 호출, 서브루틴 탈출이 한 태스크 내에서 여러 번 발생할 수 있다
⇒ OS의 인터럽트 개념과 너무 비슷하다


Coroutine 적용

fun subRoutine(){
startCoroutine{
fun1()
fun2()
...
}
}

suspend fun fun1(){}
suspend fun fun2(){}

함수 생성시 코루틴 블록 생성
탈출: return, suspend 함수 호출
호출: 서브루틴 호출, suspend 함수 종료


동시성 프로그래밍

: Coroutine은 병렬성보단 동시성에 더 가깝다


동시성 프로그래밍이란?

멀티태스킹: 와리가리 ↔ 병렬성 프로그래밍: 2배럭

⇒ 유휴시간동안 다른 프로그램 코드가 실행되도록 처리한다.
⇒ 동시성이므로 병렬프로그램과 달리 순서가 지켜진다


부수적인 내용이해

코루틴이 callback 지옥을 막는다는 이유?

“시간이 걸리는” 작업을 할 때, 순차작업의 경우 이전 작업을 기다려야한다.
기다린다 라는 것을 생으로 구현하면 완료시 넘겨준다는 것을 처리하기 위해서 콜백함수를 사용해야함을 의미한다.

fun1(... , fun2(... , fun3(... , fun4()))){}

fun1의 작업을 끝내고 fun2를 실행하기 위해서 callback(fun2)을 넘겨주며 fun1의 마지막에 fun2를 실행한다
위의 내용이 반복된다…..

따라서 코루틴을 사용하면 콜백 안의 콜백 안의 콜백 .... 이라는 지옥을 벗어날 수 있다


Context & Builder

  • CoroutineContext: Coroutine을 실행할 스레드 풀 Dispatcher.Main : UI를 구성하는 작업이 모여있는 쓰레드 풀
    Dispatcher.IO : (파일 혹은 소켓을) 읽고 쓰는 작업이 모여있는 쓰레드 풀
    Dispatcher.Default : 기본 쓰레드 풀, CPU 사용량이 많은 작업에 적합

  • CoroutineContext init: context 지정

    Dispatchers.Main

  • CoroutineScope: Coroutine 블록의 scope, Coroutine 제어영역

    • 사용자 지정 scope

      val scope = CoroutineScope(Dispatchers.Main)

      → scope 지정 가능하므로, Activity의 lifecycle과 연계가능

    • global scope → 앱 전체에서 맥락을 가지므로, 장시간 or 주기적으로 실행되어야하는 routine에 적합


  • CoroutineBuilder: 설정값을 토대로 coroutine을 실행시키는 함수

    val job = scope.launch{}

    Job, Deferred로 코루틴 제어가능



비동기처리와 코루틴에 대하여

동기/비동기 & block/nonblock 정리

: 둘을 보는 관점을 명확하게 정리해보고자..

유비추론으로 표현하면, thread와 task의 관계와 같다(like Multithread vs Multitasking)


동기/비동기

: 순차적관점 in thread

: 컴퓨터가 수행할 수 있는 가장 작은 작업의 단위인 thread순서결정여부가 지표


blocking/nonblocking

: 순차적관점 in task

: 보다 더 추상화된 계층에서의 작업단위인 task순서결정여부가 지표


용어정리

가장 작은 작업 단위 = 컴퓨터 자원을 정확히 1개 사용하는 작업의 단위 (ex 파일저장은 가장 작은 단위가 아님 = 1.“메모리”에서 데이터를 읽어서 2.“디스크”로 데이터를 써야하는 작업)

프로그래밍 언어 차원에서 가장 작은 단위: 프로그래밍 언어가 어셈블리어가 아닌 이상 당연히 언어차원에서는 ‘가장 작은 단위’의 작업을 수행할 수 없다. 따라서 프로그래밍 언어 입장에서 자원을 쓰는 가장 작은 단위가 될 것임. 쉽게 말하면 짤 수 있는 가장 로우레벨의 코드 = 강주혁이 맨날 말하는 순수함수?는 아니긴한데 그처럼 가장 로우레벨에 있을 작업

회원가입 요청함수 = 가장 작은 단위 X
ID가 중복되는지 DB로 확인해서 bool 값을 반환하는 함수 = 가장 작은 단위 X
id 값을 인자로 하는 SQL 쿼리 함수 = 가장 작은 단위 O


blocking: 순차 보장 = 하나의 작업을 마쳐야만 다른 작업 수행가능 = 스레드가 종료되기 전까지 문맥교환이 일어날 수 없다

nonblocking: 순차 보장 X = 하나의 작업을 마치지 않아도 다른 작업 수행가능


동기/비동기 by callback

: “컴퓨터가 수행할 수 있는 가장 작은 작업의 단위인 thread순서결정여부가 지표”라는 말을 이해하기 위해 callback과 System Stack으로 이해해보자

fun parent(){
child()
}

→ System Stack은 child()를 만나서 child 맥락이 push 됨
→ 문맥이 child 프로시저에 있으므로 child가 시스템 스택 사용을 마치고 반환해야만 parent가 실행가능
→ parent는 System Stack에 의해 blocking 당함 = 동기처리

— 만약 이 때 child()가 자체적으로 실행 중간에 parent로 맥락을 바꿔 진행하다 다시 child로 맥락을 바꾸게 할 수만 있다면 이는 blocking 된 것이 아니므로 단일스레드 내에서 비동기적 처리처럼 보이게 동작한다고 볼 수 있음(실제론 동시성 프로그래밍이지만) = 코루틴


fun parent(child: () -> Unit){
child()
}

→ callback을 통해서 비동기함수는 System Stack이 child()맥락으로 점프하지 않는다
→ 문맥이 여전히 parent 프로시저에 있음
→ parent는 System Stack에 있지 않으므로 blocking 당하지 않음 = 비동기처리 → 비동기 처리를 이용하면 시스템 스택이 push 되어 parent()가 블록되지 않으므로 스레드가 순차적으로 실행될 필요가 없음을 의미


blocking asynchronous 예시

ID가 중복되는지 DB로 확인해서 bool 값을 반환하는 함수

blocking :: id값 확인 → id값으로 쿼리요청 → 쿼리 확인 후 리턴값 설정
비동기처리 :: “id값으로 쿼리요청”이라는 비동기함수를 포함하므로 비동기처리로 볼 수 있음


nonblocking asynchronous 예시

난수로 생성한 10개의 ID 값으로 이벤트 당첨자 닉네임을 10개를 뽑는 함수

nonblocking :: id값으로 닉네임 쿼리요청1~10 모두 독립적, 순서가 상관이 없음
비동기처리 :: “id값으로 닉네임 쿼리요청”이라는 비동기함수를 포함하므로 비동기처리로 볼 수 있음